linux i2c设备驱动之触控芯片驱动简述

您所在的位置:网站首页 linux 触控板驱动 源码 linux i2c设备驱动之触控芯片驱动简述

linux i2c设备驱动之触控芯片驱动简述

2024-07-13 08:06| 来源: 网络整理| 查看: 265

本博客参考了https://blog.csdn.net/jklinux/article/details/74561352 ,在之前基础上扩展

现实中lcd的显示部分与触摸部分实际是分开的。

我们可以形象的理解为:电容屏的触摸相当于在lcd屏上覆盖一个透明的矩阵键盘, 当用户按下时,可以获取用按下的坐标位置. 通常情况不管是电阻还是电容屏,现都使用一个触控芯片,用于处理用户的操作坐标及实现与SOC的坐标数据的交互.

电阻的触控芯片有: tsc2046, 还有国内的: xx2046

电容的触控芯片有: ft5x06(9寸屏以下使用), gt9271(9寸屏以上用)

============

电容屏说明:主要围绕上面我们使用较多的芯片的来说

这里写图片描述注意: 屏越大, tx/rx线就越多. tx线相当于矩阵键盘里的列线, rx线相当于矩阵键盘里的行线.

这里写图片描述TP表示触摸屏上的触摸膜 Host表示我们的板 整个图的意思:表示ft5x06触控芯片, 会自动处理按下坐标的事件,会通过int脚(低电平工作)通知我们的SOC有坐标事件发生,然后我们通过i2c接口把坐标数据读取回去。也就是对我们来说, 触控芯片就是一个i2c设备.

i2c设备得有设备地址, 我用过的ft5306的地址有: 0x2b(这个在个人理解上,是很老的一批芯片使用的地址), 0x38(??), 设备地址可以用i2c_new_probed_device函数探测出来 ==================1>      ft5x06芯片内部也有很多寄存器, 寄存器上的值需要通过i2c按口来访问: 这里写图片描述

===========2>       ft5x06的i2c读写时序:

这里写图片描述其中Data Address表示芯片内部的寄存器地址 /// 这里写图片描述从触控芯片读取寄存器的值的时序. 但这里没有指定从哪个寄存器开始读。有些ft5306默认是从地址为0的寄存器开始读, 有些必须用下面时序来指定要开始读寄存器的地址才可以

这里写图片描述

//  这个用于指定开始读数据的寄存器的地址

/linux-3.4.112/drivers/input/touchscreen  可以在内核源当中找到这几种常见的触摸芯片的驱动

//电容的触控芯片有: ft5x06(9寸屏以下使用), gt9271(9寸屏以上用) ,在编程上 两者的主要使用区别在于:后者需要每下一次操作之前把寄存器的值清0(后续补贴上gt9271代码及差异之处)

====================

电阻屏的说明:

电阻的触控芯片有: tsc2046, 还有国内的: xx2046基本都是最后兼容到tsc2046芯片上。

当然还有很大一部分的芯片是自带有这部分的触摸功能的。如s3c2440 ,6818等

我们用一部分的hdmi的触摸屏都是使用的电阻屏,对应的触控芯片会有不一样(比如,树梅派的屏大致的原理是把24位色的转化成16位色,显示在屏幕上。)

使用的电阻屏,以四线电阻屏比较多。

.......

具体原理(待补上)

.......

我们能够知道,四线屏的原理是最后采集到的ADC值,最后我们需要使用移植tslib第三方库 来实现我们获得的电压值到坐标值的转换(这里的内容参考的s3c2440的lcd屏驱动的实现来描述的)。而电容屏是不需要这个步骤,直接能从触控芯片输出中得到坐标值。

====================

芯片的操作:

读取ft5306芯片内部地址为0xa3的寄存器的值:

#include #include #include int myprobe(struct i2c_client *cli, const struct i2c_device_id *id) { struct i2c_msg msgs[2]; char reg_addr = 0xa3; //读取ft5306芯片内部地址为xa3寄存器的值 char data; printk("in myprobe ...%s, %x\n", cli->name, cli->addr); // set data address msgs[0].addr = cli->addr; msgs[0].flags = 0; //write msgs[0].len = 1; //要发出的数据长度,多少字节 msgs[0].buf = ®_addr; if (i2c_transfer(cli->adapter, &msgs[0], 1) < 0) { printk("i2c_transfer0 failed ...\n"); return -ENODEV; } // read reg_addr 0xa3 msgs[1].addr = cli->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = 1; msgs[1].buf = &data; i2c_transfer(cli->adapter, &msgs[1], 1); printk("data = %x\n", data); return 0; } int myremove(struct i2c_client *cli) { printk("in myremove ...\n"); return 0; } struct i2c_device_id ids[] = { {"ft5x0x_ts"}, {}, }; struct i2c_driver mydrv = { .probe = myprobe, .remove = myremove, .id_table = ids, .driver = { .name = "myi2c", }, }; module_i2c_driver(mydrv); MODULE_LICENSE("GPL"); 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960

/// 当触屏有按下或松手事件发生时, 触控芯片会通过int脚发出通知信号. 设备驱动里在中断处理函数里读取坐标数据,但调用i2c_transfer函数会堵塞而中断处函数里不可以堵塞的, 所以用工作队列作中断的底半部,在中断的底半部里读取坐标.

实现标准的输入设备驱动,还需要在驱动加入输入设备的驱动模型. 为了方便QT程序的应用,实现单点触摸输入设备: 主要驱动模型:

input_dev初始化 mydev->name = "my ts"; mydev->evbit[0] = BIT_MASK(EV_KEY)|BIT_MASK(EV_ABS); //支持事件类型, 触摸屏的坐标是绝对坐标 mydev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);//触摸按键支持 set_bit(ABS_X, mydev->absbit); // 设置此输入设备的绝对坐标数据里有x, y, pressure数据 set_bit(ABS_Y, mydev->absbit); set_bit(ABS_PRESSURE, mydev->absbit); input_set_abs_params(mydev, ABS_X, 最小值, 最大值, 物理误差, 误差); //x轴的最小/大值 input_set_abs_params(mydev, ABS_Y, 0, 0x3ff, 0, 0); input_set_abs_params(mydev, ABS_PRESSURE, 0, 1, 0, 0); 采到坐标数据后: input_report_abs(mydev, ABS_X, x); //汇报坐标值 input_report_abs(mydev, ABS_Y, y); input_report_key(mydev, BTN_TOUCH, 1); //汇报按下 input_report_abs(mydev, ABS_PRESSURE, 1); input_sync(mydev); 注意汇报坐标值时得要汇报按下 收到up的中断后 input_report_key(mydev, BTN_TOUCH, 0); //汇报松手 input_report_abs(mydev, ABS_PRESSURE, 0); input_sync(mydev); 12345678910111213141516171819202122232425

/// test.c

#include #include #include #include #include #include #include typedef struct { struct workqueue_struct *queue; // 工作队列,用于调度工作任务 struct work_struct work; // 工作任务,用于中断底半部 struct i2c_client *cli; // i2c设备 struct input_dev *dev; //输入设备 }mywork_t; irqreturn_t ts_irq(int irqno, void *arg) { mywork_t *mywork = (mywork_t *)arg; disable_irq_nosync(irqno); //关闭中断,等中断底半部接收坐标后再重新打开中断 queue_work(mywork->queue, &mywork->work); //底半部的工作安排 return IRQ_HANDLED; } void ts_work(struct work_struct *work) //中断底半部处理函数 { mywork_t *mywork = container_of(work, mywork_t, work); struct i2c_msg msg; struct i2c_client *cli = mywork->cli; unsigned char data[32]; int x, y; msg.addr = cli->addr; msg.flags = I2C_M_RD; msg.len = 32;  //从地址为0的寄存器,连续读32个寄存器的值 msg.buf = data; if (i2c_transfer(cli->adapter, &msg, 1) < 0) { printk("i2c transfer failed ...\n"); return; } if ((data[3]>>6) == 1) //up { input_report_abs(mywork->dev, ABS_PRESSURE, 0); input_report_key(mywork->dev, BTN_TOUCH, 0); input_sync(mywork->dev); } else  //按下 { x = ((data[3]&0xf)dev, BTN_TOUCH, 1); input_sync(mywork->dev); } enable_irq(cli->irq); //重新打开中断 } int myprobe(struct i2c_client *cli, const struct i2c_device_id *id) { int ret; mywork_t *mywork; struct input_dev *dev; mywork = kzalloc(sizeof(*mywork), GFP_KERNEL); //准备i2c设备的数据 mywork->cli = cli; mywork->queue = create_singlethread_workqueue("myts"); //创建工作队列 INIT_WORK(&mywork->work, ts_work); //初始化工作任务 dev = input_allocate_device(); //输入设备分配空间 //初始化输入设备 dev->name = "myts", dev->evbit[0] = BIT_MASK(EV_KEY)|BIT_MASK(EV_ABS); dev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); input_set_abs_params(dev, ABS_X, 0, 800, 0, 0); input_set_abs_params(dev, ABS_Y, 0, 480, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0); dev->absbit[BIT_WORD(ABS_X)] |= BIT_MASK(ABS_X); dev->absbit[BIT_WORD(ABS_Y)] |= BIT_MASK(ABS_Y); dev->absbit[BIT_WORD(ABS_PRESSURE)] |= BIT_MASK(ABS_PRESSURE); mywork->dev = dev; input_register_device(dev); //注册输入设备 dev_set_drvdata(&cli->dev, mywork); ret = request_irq(cli->irq, ts_irq, IRQF_TRIGGER_FALLING, "myts", mywork); //申请中断 return ret; } int myremove(struct i2c_client *cli) { mywork_t *mywork = dev_get_drvdata(&cli->dev); free_irq(cli->irq, mywork); cancel_work_sync(&mywork->work); destroy_workqueue(mywork->queue); input_unregister_device(mywork->dev); printk("in myremove ...\n"); return 0; } struct i2c_device_id ids[] = { {"ft5x0x_ts"}, {}, }; struct i2c_driver mydrv = { .probe = myprobe, .remove = myremove, .id_table = ids, .driver = { .name = "myi2c", }, }; moudle_i2c_driver(mydrv); MODULE_LICENSE("GPL");



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3